import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PaymentOrder } from '@/database/entities/payment-order.entity';
import * as crypto from 'crypto';
import * as axios from 'axios';

@Injectable()
export class WechatService {
  private readonly appId: string;
  private readonly mchId: string;
  private readonly apiKey: string;
  private readonly gateway: string = 'https://api.mch.weixin.qq.com';

  constructor(private configService: ConfigService) {
    this.appId = this.configService.get('WECHAT_APP_ID');
    this.mchId = this.configService.get('WECHAT_MCH_ID');
    this.apiKey = this.configService.get('WECHAT_API_KEY');
  }

  async createPayment(order: PaymentOrder): Promise<string> {
    const params = {
      appid: this.appId,
      mch_id: this.mchId,
      nonce_str: this.generateNonceStr(),
      body: order.description || '商品支付',
      out_trade_no: order.orderNo,
      total_fee: Math.round(order.amount * 100), // 转换为分
      spbill_create_ip: order.clientIp || '127.0.0.1',
      notify_url: `${this.configService.get('BACKEND_URL')}/api/payment/callback/wechat`,
      trade_type: 'NATIVE', // 扫码支付
    };

    const sign = this.generateSign(params);
    params['sign'] = sign;

    const xml = this.objectToXml(params);

    try {
      const response = await axios.default.post(`${this.gateway}/pay/unifiedorder`, xml, {
        headers: { 'Content-Type': 'application/xml' },
      });

      const result = this.xmlToObject(response.data);
      
      if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
        return result.code_url; // 返回二维码链接
      } else {
        throw new Error(result.return_msg || result.err_code_des);
      }
    } catch (error) {
      throw new Error(`微信支付创建订单失败: ${error.message}`);
    }
  }

  async verifyCallback(xmlData: string): Promise<any> {
    const data = this.xmlToObject(xmlData);
    
    const sign = data.sign;
    delete data.sign;

    const calculatedSign = this.generateSign(data);
    
    if (sign !== calculatedSign) {
      throw new Error('微信支付回调签名验证失败');
    }

    return data;
  }

  async refund(order: PaymentOrder, reason?: string): Promise<any> {
    const params = {
      appid: this.appId,
      mch_id: this.mchId,
      nonce_str: this.generateNonceStr(),
      out_trade_no: order.orderNo,
      out_refund_no: `RF${order.orderNo}`,
      total_fee: Math.round(order.amount * 100),
      refund_fee: Math.round(order.amount * 100),
      refund_desc: reason || '用户申请退款',
    };

    const sign = this.generateSign(params);
    params['sign'] = sign;

    const xml = this.objectToXml(params);

    try {
      const response = await axios.default.post(`${this.gateway}/secapi/pay/refund`, xml, {
        headers: { 'Content-Type': 'application/xml' },
        // 这里应该使用客户端证书进行双向认证
      });

      const result = this.xmlToObject(response.data);
      
      return {
        success: result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS',
        refund_fee: result.refund_fee,
        out_trade_no: result.out_trade_no,
        error: result.return_msg || result.err_code_des,
      };
    } catch (error) {
      return {
        success: false,
        error: error.message,
      };
    }
  }

  private generateSign(params: any): string {
    const sortedParams = Object.keys(params)
      .sort()
      .filter(key => params[key] !== undefined && params[key] !== '')
      .map(key => `${key}=${params[key]}`)
      .join('&');

    const stringSignTemp = `${sortedParams}&key=${this.apiKey}`;
    
    return crypto
      .createHash('md5')
      .update(stringSignTemp, 'utf8')
      .digest('hex')
      .toUpperCase();
  }

  private generateNonceStr(): string {
    return crypto.randomBytes(16).toString('hex');
  }

  private objectToXml(obj: any): string {
    let xml = '<xml>';
    for (const key in obj) {
      if (obj[key] !== undefined) {
        xml += `<${key}><![CDATA[${obj[key]}]]></${key}>`;
      }
    }
    xml += '</xml>';
    return xml;
  }

  private xmlToObject(xml: string): any {
    const obj = {};
    const regex = /<(\w+)><!\[CDATA\[(.*?)\]\]><\/\w+>/g;
    let match;
    
    while ((match = regex.exec(xml)) !== null) {
      obj[match[1]] = match[2];
    }
    
    // 处理没有CDATA的标签
    const simpleRegex = /<(\w+)>([^<]*)<\/\w+>/g;
    while ((match = simpleRegex.exec(xml)) !== null) {
      if (!obj[match[1]]) {
        obj[match[1]] = match[2];
      }
    }
    
    return obj;
  }
}